#include "fat32.h"

#include "../card/card_io.h"

#pragma pack(1)
typedef struct 
{
	u32	fat_begin;
	u32 cluster_begin;
	u8 sec_per_cluster;
	u32 root_dir;
}TPartitionInfo;
#pragma pack()



static TPartitionInfo m_partInfo;
static u32 m_cluster;
static u16 m_clusterPos;

static void setCluster(u32 cluster)
{
	cardIO_seek(m_partInfo.cluster_begin + (cluster-2) * m_partInfo.sec_per_cluster, 0);
	m_cluster = cluster;
	m_clusterPos = 0;
}

static void nextCluster()
{
	u32 sector = m_cluster >> 7;
	u16 pos = (m_cluster & 0x7F) << 2;

	cardIO_seek(m_partInfo.fat_begin + sector, pos);

	setCluster(cardIO_readU32());
}


bool fat32_init()
{
	if (!cardIO_init())
		return FALSE;

	//goto first sector
	cardIO_seek(0, 0);

	//skip the boot code
	cardIO_skip(446);

	//read the four partitions, searching for a FAT32
	u32 lba = 0;
	for (int i=0;i<4;i++)
	{
		cardIO_skip(4);
		u8 ptype = cardIO_readU8();
		cardIO_skip(3);
		u32 plba = cardIO_readU32();
		cardIO_skip(4);

		if ((lba == 0)&&((ptype == 0x0B)||(ptype == 0x0C)))
		{
			lba = plba;
		}
	}

	if (lba == 0)
		return FALSE;	//no compatible partition found
	
	//read signature
	u16 sig = cardIO_readU16();

	if (sig != 0xAA55)
		return FALSE;	//wrong signature

	//goto the start of the partition
	cardIO_seek(lba, 0);

	cardIO_skip(11);
	u16 bytes_per_sector = cardIO_readU16();
	u8 sectors_per_cluster = cardIO_readU8();
	u16 reserved_sectors = cardIO_readU16();
	u8 fat_copies = cardIO_readU8();

	cardIO_skip(19);
	u32 fat_sector_size = cardIO_readU32();
	cardIO_skip(4);
	u32 root_first_cluster = cardIO_readU32();

	cardIO_skip(462);
	sig = cardIO_readU16();

	if (sig != 0xAA55)
		return FALSE;	//wrong signature

	m_partInfo.fat_begin = lba + reserved_sectors;
	m_partInfo.cluster_begin = m_partInfo.fat_begin + fat_copies * fat_sector_size;
	m_partInfo.sec_per_cluster = sectors_per_cluster;
	m_partInfo.root_dir = root_first_cluster;

	return TRUE;
}

void fat32_read(void *buf, u32 size)
{
	u8* b = (u8*) buf;
	
	for (u32 i=0; i<size; i++)
	{
		*b = cardIO_readU8();
		b++;
		m_clusterPos++;

		if (m_clusterPos == m_partInfo.sec_per_cluster * 512)
		{
			nextCluster();
		}
	}
}

void fat32_findFirst(TFileInfo *dir)
{
	fat32_open(dir);
}

static bool readEntry(TFileInfo *info)
{
	fat32_read(&info->fname, 11);
	info->fname[11] = 0;
	info->attr = fat32_readU8();
	fat32_skip(8);
	u32 clusterHI = fat32_readU16();
	fat32_skip(4);
	u32 clusterLO = fat32_readU16();
	info->size = fat32_readU32();

	info->cluster = (clusterHI << 16) | clusterLO;
	return (info->fname[0] != 0);
}

bool fat32_findNext(TFileInfo *dir)
{
	while (readEntry(dir))
	{
		if (((dir->attr & 0x0F) != 0x0F)&&(dir->fname[0] != 0xE5)&&(dir->attr != 0x08))
			return TRUE;
	}

	return FALSE;
}

bool fat32_open(TFileInfo *dir)
{
	if (!dir)
	{
		setCluster(m_partInfo.root_dir);
	}
	else
	{
		setCluster(dir->cluster);
	}

	return TRUE;
}

u8 fat32_readU8()
{
	u8 v;

	fat32_read(&v, sizeof(v));

	return v;
}

u16 fat32_readU16()
{
	u16 v;

	fat32_read(&v, sizeof(v));

	return v;
}

u32 fat32_readU32()
{
	u32 v;

	fat32_read(&v, sizeof(v));

	return v;
}

void fat32_skip(u32 count)
{
	for (u32 i=0; i<count; i++)
	{
		fat32_readU8();
	}
}
